#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/namei.h>
#include <linux/dirent.h>
#include <linux/kobject.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/skbuff.h>
#include <linux/if_pppox.h>
#include <linux/list.h>
#include <linux/ptrace.h>
#include <linux/spinlock.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <net/tcp.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/dma.h>


#if 0
static struct irqaction *S3C2440_rx_action = NULL;
static struct irqaction *S3C2440_tx_action = NULL;

static void S3C2440_free_irq(int irq, struct irqaction **old)
{
	struct irqaction *p;
/*  todo
    unsigned long flags;
	if (irq >= NR_IRQS || !irq_desc[irq].valid) {
		printk(KERN_ERR "Trying to free IRQ%d\n",irq);
		dump_stack();
		return;
	}
	spin_lock_irqsave(&irq_controller_lock, flags);
*/

    p = irq_desc[irq].action;
    if (p == NULL) {
        printk(KERN_ERR "Trying to free IRQ%d, but action == NULL\n",irq);
        return;
    }
    if (p->next != NULL) {
        printk(KERN_ERR "Trying to free IRQ%d, but action->next != NULL\n",irq);
        return;        
    }
    *old = p;
    irq_desc[irq].action = NULL;
/* todo
    spin_unlock_irqrestore(&irq_controller_lock, flags);
*/    
}

static void S3C2440_restore_irq(int irq, struct irqaction **old)
{
/*  todo
    unsigned long flags;
	if (irq >= NR_IRQS || !irq_desc[irq].valid) {
		printk(KERN_ERR "Trying to free IRQ%d\n",irq);
		dump_stack();
		return;
	}
	spin_lock_irqsave(&irq_controller_lock, flags);
*/
    if (irq_desc[irq].action != NULL) {
        printk(KERN_ERR "Trying to restore IRQ%d, but action != NULL\n",irq);
        return;
    }
    irq_desc[irq].action = *old;
    *old = NULL;

/* todo
    spin_unlock_irqrestore(&irq_controller_lock, flags);
*/    

}

#endif

static void S3C2440_free_uart_irq(void)
{
    disable_irq(IRQ_S3CUART_RX0);
    disable_irq(IRQ_S3CUART_TX0);    

/*    
    if (S3C2440_rx_action == NULL)
        S3C2440_free_irq(IRQ_S3CUART_RX0, &S3C2440_rx_action);
    if (S3C2440_tx_action == NULL)
        S3C2440_free_irq(IRQ_S3CUART_TX0, &S3C2440_tx_action);
*/
}

static void S3C2440_restore_uart_irq(void)
{
    enable_irq(IRQ_S3CUART_RX0);
    enable_irq(IRQ_S3CUART_TX0);    
/*
    if (S3C2440_rx_action != NULL) {
        S3C2440_restore_irq(IRQ_S3CUART_RX0, &S3C2440_rx_action);
    }

    if (S3C2440_tx_action != NULL) {
        S3C2440_restore_irq(IRQ_S3CUART_TX0, &S3C2440_tx_action);        
    }
*/
}

static void dump_conf_byte(void *addr)
{
    printk(KERN_DEBUG "%02x\n", __raw_readb(addr));
    return;
}

static void dump_conf_word(void *addr)
{
    printk(KERN_DEBUG "%04x\n", __raw_readw(addr));
    return;
}

static void dump_conf_long(void *addr)
{
    printk(KERN_DEBUG "%08x\n", __raw_readl(addr));
    return;
}

#define S3C2410_URXH	  (0x24)
#define S3C2410_UTXH	  (0x20)
#define S3C2410_ULCON	  (0x00)
#define S3C2410_UCON	  (0x04)
#define S3C2410_UFCON	  (0x08)
#define S3C2410_UMCON	  (0x0C)
#define S3C2410_UBRDIV	  (0x28)
#define S3C2410_UTRSTAT	  (0x10)
#define S3C2410_UERSTAT	  (0x14)
#define S3C2410_UFSTAT	  (0x18)
#define S3C2410_UMSTAT	  (0x1C)

/* DMA controller */
#define S3C24XX_VA_DMA	   (0xc4800000)
#define S3C2410_PA_UART	   (0x50000000)

char transfer_data[100];

static void *uart_v_addr = NULL;

int S3C2440_uart_init(void)
{
    int i;
    dma_addr_t phy;
    char *vir;
/*    S3C2440_free_uart_irq();  */
    printk(KERN_DEBUG "%s init\n", __FUNCTION__);

    uart_v_addr = (void *)0xF0000000 + 0x00800000; /* S3C24XX_VA_UART; */

    __raw_writeb('t', (uart_v_addr + S3C2410_UTXH));
    __raw_writeb('p', (uart_v_addr + S3C2410_UTXH));
    __raw_writeb('l', (uart_v_addr + S3C2410_UTXH));

/*
    dump_conf_byte(uart_v_addr + S3C2410_ULCON);
    dump_conf_byte(uart_v_addr + S3C2410_UCON);
    dump_conf_byte(uart_v_addr + S3C2410_UFCON);
    dump_conf_byte(uart_v_addr + S3C2410_UMCON);
    
    dump_conf_byte(uart_v_addr + S3C2410_UTRSTAT);
    dump_conf_byte(uart_v_addr + S3C2410_UERSTAT);
    dump_conf_byte(uart_v_addr + S3C2410_UFSTAT);
    dump_conf_byte(uart_v_addr + S3C2410_UMSTAT);
    dump_conf_byte(uart_v_addr + S3C2410_UBRDIV);

    printk(KERN_DEBUG "=======================\n");

    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRC);
    dump_conf_byte((void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRCC);
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DIDST);
    dump_conf_byte((void *)S3C24XX_VA_DMA + S3C2410_DMA_DIDSTC);
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DCON);
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DSTAT);        
    dump_conf_byte((void *)S3C24XX_VA_DMA + S3C2410_DMA_DMASKTRIG);    
*/

	vir = (char *)dma_alloc_coherent(NULL, 100 ,&phy, GFP_KERNEL|GFP_DMA);    
    for (i = 0; i < sizeof(transfer_data); ++i) {
        vir[i] = '1' + i%10;
    }
//    writel(transfer_data, (void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRC);
//    writel(__pa(&transfer_data), (void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRC);
    writel(phy, (void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRC);        
    writeb(0x0, (void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRCC);

    writel(S3C2410_PA_UART + S3C2410_UTXH, (void *)S3C24XX_VA_DMA + S3C2410_DMA_DIDST);
    writeb(0x7, (void *)S3C24XX_VA_DMA + S3C2410_DMA_DIDSTC);    

    writel(1<<28 | 1<<22 | (sizeof(transfer_data)), (void *)S3C24XX_VA_DMA + S3C2410_DMA_DCON);

    writel(3 , (void *)S3C24XX_VA_DMA + S3C2410_DMA_DMASKTRIG);    

    for (;;) {
        i = readl((void *)S3C24XX_VA_DMA + S3C2410_DMA_DSTAT);
        if ((i & 0xfffff) == 0)
            break;
        writel(3 , (void *)S3C24XX_VA_DMA + S3C2410_DMA_DMASKTRIG);    
    }
    
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRC);
    dump_conf_byte((void *)S3C24XX_VA_DMA + S3C2410_DMA_DISRCC);
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DIDST);
    dump_conf_byte((void *)S3C24XX_VA_DMA + S3C2410_DMA_DIDSTC);
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DCON);
    dump_conf_byte((void *)S3C24XX_VA_DMA + S3C2410_DMA_DMASKTRIG);        

    printk(KERN_DEBUG "=======================\n");
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DSTAT);
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DCSRC);        
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DCDST);    
    return 0;
}


void S3C2440_uart_exit(void)
{
/*    S3C2440_restore_uart_irq();  */
	printk(KERN_DEBUG "%s exit\n", __FUNCTION__);

    dump_conf_byte((void *)S3C24XX_VA_DMA + S3C2410_DMA_DMASKTRIG);        

    printk(KERN_DEBUG "=======================\n");
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DSTAT);
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DCSRC);        
    dump_conf_long((void *)S3C24XX_VA_DMA + S3C2410_DMA_DCDST);    

    uart_v_addr = NULL;
}

module_init(S3C2440_uart_init);
module_exit(S3C2440_uart_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("tpl.a1406@gmail.com");

